<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>盒子模型</title>
</head>
<style>
    h1,
    h2,
    h3,
    h4,
    h5,
    h6 {
        color: brown;
    }

    h1 {
        margin: 10px;
        padding: 10px;
        background-color: burlywood;
        text-align: center;
    }

    p {
        text-indent: 2em;
    }

    em {
        color: brown;
        font-weight: bold;
    }

    img {
        margin: 10px;
        width: 70%;
    }
</style>

<body>
    <h1>盒子模型</h1>
    <h2>盒子模型的基本机制</h2>
    <p>对于html的标签而言，如果要占据屏幕空间，它通常都被处理为一个个的矩形盒子，
        换一句话，也可以说：<em>HTML，一切皆盒子。</em>
    </p>
    <p>这些盒子，就像俄罗斯套娃，盒子里面还可以放盒子。最大的盒子，当然就是根元素了，也就是html元素。
        html元素下面，套了个body小套娃，body套娃下面，套了h1套娃，p套娃，等等。当然，各级的套娃可以再
        套更小的套娃，就构成了一颗套娃树。
    </p>
    <p>对于根节点套娃，它宽度是浏览器窗口的宽度，高度随它里面的具体内容而不同。极端情况，如果里面啥内容也没有，
        那么，它的高度就是0 （W3C content-box）。但通常，它里面至少有个空body元素，这个元素通常会有个默认的外边距，
        在chorme浏览器下，外边距塌缩后，会撑高根节点8个像素。
    </p>
    <p>对于任何的非根节点套娃（元素），它总是放在<em>容器</em>（父节点）中的。它和自己的兄弟姐妹，占用父节点的空间。
        基本的占用方式有两种：</p>
    <ul>
        <li>inline: 像排队一样，一行行从前往后排，自动换行且不产生换行符。</li>
        <li>block: 从上往下排，每个block元素独占水平空间。就像挂一串灯笼一样。
            即使设置了这个节点的宽度，它小于容器的宽度，它也不允许其他节点分享剩下的空间。
        </li>
    </ul>
    <p>在父节点那里占了地儿，套娃的问题又来了，自己有好多亲儿子，要占自己盒子的内部空间，咋排呢？
        嗯，那就看这个亲儿子是inline类型的亲儿子，还是block类型的亲儿子了。如果是inline的就按行排，
        如果是block的就按块排，和前面说的排法是一样的。
    </p>
    <p>所以，浏览器显示网页，从原理上讲，就是排盒子。实际的情况，考虑实际排版的需要，要复杂得多。</p>
    <p>下面，我们看看盒子本身：</p>
    <!--img标签默认是inline显示模式，改为block模式，auto左右边界居中-->
    <img style="display: block; margin: auto;" src="/image/1897666-8f59759b6d80ad32.webp">
    <img style="margin: auto;display:block;" src="/image/1897666-1c90f0f875b4cf59.webp">
    <p>上面两幅图，第一幅是W3C的标准模型（content-box），第二幅是IE9以下的（border-box）：</p>
    <ul>
        <li>content-box,宽高按照盒子里面的content计算；border-box，宽高按照边框的宽高计算，一般浏览器默认是content-box。</li>
        <li>盒子由外边距margin、边框border、内边距padding、内容content，构成。其中前三项可以上下左右分别设置。</li>
        <li>margin: 就是外边距，可以理解为最小安全距离，因此是透明的。考虑如下情况：
            <ul>
                <li><em>水平外边距不塌缩，垂直外边距塌缩。</em>考虑塌缩的三种情况：
                    <ul>
                        <li><em>相邻塌陷：</em>当一个元素出现在另一个元素上面时，第一个元素的底边界与第二个元素的顶边界发生合并。
                            两个block上下排，上面block说，你得离我50px及以上（margin:50px;）,下面block说，
                            你得离我30px及以上（margin:30px），那么，实际显示，两个block的垂直距离到底多少呢？<br>
                            显然，50px就满足两者的要求了，50+80px也是满足要求的，但是此时两者的外边距会塌缩，
                            <em>取其中较大的50px，此时发生了塌缩</em>。如果外边距都是负值，则取绝对值较大的；如果是正负两值，
                            则取两者之和。
                        </li>
                        <img src="image/9390006-af10dec6dc1896f1.webp">
                        <li><em>包含塌陷：</em>当一个元素包含在另一个元素中时（假设没有内边距或边框把外边距分隔开），它们的顶和底边界也发生合并。
                            即内部元素的外边距由于没有padding和border的阻隔，穿出了box。
                        </li>
                        <img src="image/9390006-2e428c45b98c7b42.webp">
                        <li><em>自身塌陷：</em>当一个元素内容为空，没有padding和border，它的上下外边距重合，发生塌陷，取其大者。</li>
                        <img src="image/9390006-3f9acd4878785e77.webp">
                    </ul>
                </li>
                <li><em>*创建BFC消除塌缩：</em>bfc，又叫Block Formatting Context，就是块级格式化上下文。
                    默认元素的排版都是在标准文档流的bfc里面排的，我们称为静态编排，会发生上述塌缩的情况。
                    但是，如果元素产生了自己的bfc，那么它相对于外部就不会塌缩了。因为:
                    <em>BFC 在页面上是一个独立的容器，容器里面的子元素不会影响到外面的元素，反之亦然。</em>
                    bfc的主要特性如下：
                    <ul>
                        <li>处于同一个 BFC 中的元素相互影响，可能会发生 margin collapse；</li>
                        <li>BFC 在页面上是一个独立的容器，容器里面的子元素不会影响到外面的元素，反之亦然；</li>
                        <li>计算 BFC 的高度时，考虑 BFC 所包含的所有元素，<em>包括浮动元素也参与计算</em>；</li>
                        <li>浮动盒的区域不会叠加到 BFC 上。这有别于float的默认行为，float会造成盒子重叠，
                            里面的inline-box和inline-block绕排，因为重叠了才存在绕的问题。但是如果元素创建了bfc，
                            它就保证自己不会和同容器内的float盒子重叠。
                            <div style="border: 3px solid gray; width: 200px;margin: 10px;">
                                <div
                                    style="border:1px solid green; width:50px; height:80px;float:left;color:darkgreen;">
                                    浮动的第一个div</div>
                                <div style="border: 1px solid brown;color:brown;">
                                    <p>容器里的第二个div，里面有很多文字。
                                        这些文字环绕了浮动的盒子。</p>
                                    <p>即使多个p标签的内容，也是环绕的。</p>
                                </div>
                            </div>
                            <div style="border: 3px solid gray; width: 200px;margin: 10px;">
                                <div
                                    style="border:1px solid green; width:50px; height:80px;float:left;color:darkgreen;">
                                    浮动的第一个div</div>
                                <div style="border: 1px solid brown;color:brown;overflow: hidden;">
                                    <p>容器里的第二个div，里面有很多文字。
                                        设置了overflow:hidden;导致创建了bfc。</p>
                                    <p>此时，它就不再环绕了。</p>
                                </div>
                            </div>
                        </li>
                    </ul>
                </li>
            </ul>
        </li>
    </ul>
    <h2>浮动float的再分辨</h2>
    <p>如前所述，元素就是盒子，可以一个个按行往下排，到行末自动换行不产生换行符；
        也可以一个个按列往下排，一个盒子一行，不共享水平空间。</p>
    <p>在网页设计的早期，人们遇到的图文混排的问题，需要文字环绕图片，于是发明了float。
        它的思路很简单，按行排的盒子，遇到float的元素，就环绕排，按列排的盒子，仍然独占水平空间，
        由于float不在文档流里，不参与高度占用，因此其后面的列盒子可能会补占原来float占去的行，
        于是其盒子可能会和float发生重叠，该列盒子里面的按行排列的盒子，会绕排。
        前面的例子已经可以清晰地说明这个效果。
    </p>
    <p>可以简述为：<em>float会和文档流里面的block发生重叠，同时会绕排 inline 盒子和 inline-block 盒子。</em></p>
    <div style="border: 1px solid gray; width: 300px;">
        <div style="float:left;width:40px;background-color: burlywood;">float
            <p style="text-indent: 0%;font-size: 0.5em;">左浮动元素不参与父节点高度计算</p>
        </div>
        <div style="width: 150px;background-color: red;">div1
            <p style="text-indent:0%;">里面的p标签文字绕排</p>
            <div style="width: 100px;border: 1px solid; background-color: green;">div1下subdiv设置了宽度，
                仍然重叠，绕排。</div>
        </div>
        <div style="width: 60px;background-color: lightgreen;">div2</div>
        <div style="width: 90px;background-color:lightblue;">div3</div>
    </div>
    <p>所以，<em>float的源生意义是实现按行排的盒子对它的绕排，而非用于布局。</em>
        但早期人们进行网页布局，例如分开左边栏、右边栏、顶栏和底栏几个区域，缺乏有效的方法，
        于是就用float方法进行布局，如果查阅文献，很多都介绍如何用float布局（layout）页面。
        从最佳实践的角度，float应该仅仅用于绕排。<em>
        </em>
    </p>
    <p>在绕排情况下，float父元素的实际显示高度，就不能简单将block元素高度加上float元素高度来进行计算，
        因为文字环绕了，此时，<em>float脱离了文档流，其高度不参与父元素高度的计算。</em>在极端情况下，
        父节点的高度甚至可以变为0：
    </p>
    <div style="border: 1px solid gray;width:200px;">
        <div style="float:left;width:100px;border:1px solid brown;">左浮动节点</div>
    </div>
    <div style="clear: both;"></div>
    <p>父节点在显示上没有包含子节点在布局时可能会成为bug，我们的做法是，在float元素后再加个空元素,并要求其左右
        都没有浮动元素，即clear:both。这样这个元素会显示到浮动元素后面，而这个元素在文档流里，
        会参与高度计算，就撑开了父节点的高度。</p>
    <div style="border: 1px solid gray;width:200px;">
        <div style="float:left;width:100px;border:1px solid brown;">左浮动节点</div>
        <p style="clear: both;"></p>
    </div>
    <h2>参考文献</h2>
    <ol>
        <li>https://www.jianshu.com/p/eb5061310246</li>
        <li>https://www.jianshu.com/p/d21022eda1f5</li>
    </ol>
</body>

</html>